public class Volatile {

  /*  volatile 多个处理器环境下保证共享变量的可见性
  volatile->LOCK混合指令（缓存锁）->达到内存屏障实现可见性

    底层实现:
    hsdis:多了一个lock的汇编指令，基于总线锁或缓存锁达到效果
    可见性:硬件，JMM
    CPU,IO,内核
    绝大多数程序中，依靠CPU,内存,IO会降低程序性能
    提升:最大化利用cpu资源的三种方式:
    1.cpu增加高速缓存
    2.引入线程，进程，通过cpu时间片的切换
    3.指令优化，重排序
    三级缓存:
    高速缓存带来一致性问题:
    CPU层面的解决方案（硬件层面上提供的方式）
    1.总线锁：通过在总线上加上LOCK#锁
    2.缓存锁：通过缓存一致性协议

    缓存一致性协议（MESI）缓存行的四种状态达到缓存数据的一致,会带来CPU层面的阻塞，这时可以引入storebuffer让CPU将数据写入storebuffer中，让CPU误以为数据已经同步了来防止CPU层面的阻塞。
    {主内存：i=0   CPU0:cache0 i=1(可能会变成独占状态Exclusive，   修改状态Modify),  CPU1:cache1 i=0(变成失效状态invalid)}
    ----写入缓存时必须处于ME状态，或者使得其他cpu缓存处于失效状态，失效后把数据同步到主内存----
   提升CPU利用效率：首先数据是S状态，在CPU写入数据时，变为I状态，之后再把数据写入到storebuffer中，写入之后会storebuffer会发送invalidate消息使得其他缓存了该缓存行的CPU失效这是一个异步化的过程，
   异步化会出现可见性问题：当storebuffer中写入数据时，需要得到其他其他CPU的通知才能把数据同步到缓存行在同步到主内存中，若没有同步到主内存中也会发生可见性问题。
   CPU的异步执行会导致乱序执行的问题，CPU的异步执行->重排序->可见性问题
   可见性问题: 当storebuffer中的数据没有同步到主内存中，处于Modify修改状态，其他CPU无法加载，就会导致CPU的缓存数据不一致，
   CPU提供了内存屏障指令:可以在适当的地方加入内存屏障指令，使得CPU直接与主内存进行交互。解决可见性问题。

   内存屏障: 写屏障、读屏障、全屏障
    写屏障:告知处理器在写屏障之前存储在storebuffer的指令同步到主内存中
    读屏障（失效队列）:处理器在加了读屏障之后的操作保证能够读取到最新的数据
    全屏障:读屏障和写屏障配合使用，为了解决可见性

1、修改（Modify）
高速缓存行仅存在于当前高速缓存中，并且是脏的 - 它已从主存储器中的值修改（M状态）。在允许对（不再有效）主存储器状态的任何其他读取之前，需要高速缓存在将来的某个时间将数据写回主存储器。回写将该行更改为共享状态（Share）。
描述: 当CPU接收到来自storebuffer的失效通知时，会返回ACK值使得需要写数据的CPU变成Modify状态。{主内存：i=0   CPU0:cache0 i=1, storebuffer i=1  CPU1:cache1 i=0}

2、独占（Exclusive）
缓存行仅存在于当前缓存中，但是干净 - 它与主内存匹配。它可以随时更改为共享状态，以响应读取请求。或者，可以在写入时将其改变为修改状态(Modify)。
描述: 该Cache line有效，数据和内存中的数据一致，数据只存在于本Cache中。{主内存：i=1   CPU0:cache0 i=1,  CPU1:cache1 i=0}

3、共享（Share）
表示此高速缓存行可能存储在计算机的其他高速缓存中并且是干净的 - 它与主存储器匹配。可以随时丢弃该行（更改为无效状态）。
描述: 数据可能已经被多个CPU缓存了，各个缓存行的数据和组内存的数据保持一致。 {主内存：i=0   CPU0:cache0 i=0,  CPU1:cache1 i=0}

4、无效（invalid）
表示此缓存行无效（未使用）。
描述: 在storebuffer写入数据以后会告知Invalid消息使得其他缓存了该缓存行的CPU失效。 {主内存：i=0   CPU0:cache0 i=1, storebuffer i=1,  CPU1:cache1 i=0}

JMM是一种抽象的内存模型：
三大特性：原子性、可见性、有序性
核心的价值是解决了有序性，可见性
语言级别的抽象内存模型：volatile,synchronized,final,（happens-before）
指令重排的例子：
1、int a=1;
2、int b=1;
3、int c=a+b;
执行顺序：123 ， 213
对此我们可以通过优化指令的运行顺序来提高提高CPU的利用率
源代码中有两个重排序方式：
在单线程中,有数据依赖规则：无论怎么进行重排序，执行的结果不会变
在多线程中，提供了内存屏障来解决可见性问题

1.编译器级别（实现语言级别的内存屏障类型）：
2.CPU重排(指令集，内存)
关于四种内存屏障的类型
在进行读/写操作时，加入了内存屏障用于保证读/写操作的执行顺序
（读屏障）LoadLoad    Load1; LoadLoad; Load2	保证load1的读取操作在load2及后续读取操作之前执行
（写屏障）StoreStore   Store1;StoreStore; Store2   在store2及其后的写操作执行前，保证store1的写操作已刷新到主内存
（全屏障）LoadStore    Load1; LoadStore; Store2	在stroe2及其后的写操作执行前，保证load1的读操作已读取结束
（全屏障）StoreLoad    Store1; StoreLoad; Load2	保证store1的写操作已刷新到主内存之后，load2及其后的读操作才能执行

Happens-before规则
在jdk1.5java内存模型中引入了Happens-before原则
除了volatile之外解决可见性问题的方法
A Happens-before B : A操作的结果对B操作可见，是一种A和B的关系
具体规则：
(1)程序顺序规则：一个线程中的每个操作，happens-before于该线程中的任意后续操作。
(2)监视器锁规则：对一个锁的解锁，happens-before于随后对这个锁的加锁。
(3)volatile变量规则：对一个volatile域的写，happens-before于任意后续对这个volatile域的读。
(4)传递性：如果A happens-before B，且B happens-before C，那么A happens-before C。
(5)start()规则：如果线程A执行操作ThreadB.start()（启动线程B），那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。
(6)Join()规则：如果线程A执行操作ThreadB.join()并成功返回，那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
(7)程序中断规则：对线程interrupted()方法的调用先行于被中断线程的代码检测到中断时间的发生。
(8)对象finalize规则：一个对象的初始化完成（构造函数执行结束）先行于发生它的finalize()方法的开始。


    */


    public volatile static boolean stop =false;


    public static void main(String[] args) throws InterruptedException {
    //构建一个线程，定义i=0,做循环
    Thread t1=new Thread(()->{
        int i=0;
        while (!stop){//条件不满足，没有终止
            i++;
        }

    });
    t1.start();
    //睡眠1000ms,在修改stop
    Thread.sleep(1000);
    stop=true;

    }
}
